package com.ruoyi.business.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import com.ruoyi.business.ajax.request.ZfbNotifyRequest;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.util.LogUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

//微信支付工具类
public class PayUtil {

    //微信支付app_id
    public static final String app_id = "wx0a54943f480f535d";
    public static final String app_secret = "be6f394b20ef8e281facb92c8813ba4d";
    //微信支付mch_id
    public static final String mch_id = "1603111863";
    //证书相关
    //public static final String cert_file = "E:\\certificate\\apiclient_cert.p12";
    public static final String cert_file = "/home/cert/apiclient_cert.p12";
    public static final String cert_serial = "611AC351502BD7D89ABF089D84AB85FF416834F5";
    public static final String apiV3Key = "0e6976f90ae211eba50c0242ac110004";
    public static final String sign_format = "WECHATPAY2-SHA256-RSA2048";
    //微信支付平台证书(定时更新)
    public static String wx_cert_serial = "4C6B2F1D4CA4125D61E6D5ABACC5923D9";
    //微信支付平台签名工具
    private static Signature wx_signature = null;
    //支付宝卖家信息
    private static String zfb_app_id = "2021001197676800";
    private static String zfb_seller_id = "2088931495703072";
    private static String zfb_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVfnFCPXCVRfz04hAMiS46LaPIq0rJUdm0y7m+JVwY2wYfraHyhU7aIFnPVkIlttZlcBuxLxJUN19oYNyt1K4gGauhdgA0kcJL9bbo2+jRSFWrb8WStGz6StagWWc5aahCXNsM5jBM+7VFEOCgnnnJHrE5S8OmSX/OWL+YISzcsNUxY6pODo4NlpPKofVgjd3VQbNGkXPiMRlrOG1gae3VPF2NvgWnAyaiWTuTyXrtbZgtKUFnbqo15hZPPoIkXUa17MdtdEsjBsjet8q1LGnOdE3fznaWFbOyeJ/8psaeQhMq41sJjgm/yBY13dIZyd+BLfs4J0zkMbxnsvBgkr9QIDAQAB";
    private static String zfb_private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG0uTMfp55FQ+7liXkHFB67arGP+4kTkg/aGECYDqQv9fPQiLjCZxh4NzWRJQ4IHAWWnSc0qFOsXxwDcIrn5O34y9IDv206rYxvD0Z4+WwSh1PMFHouK+qACEV8MAE2slviwPdMQbJoHuNSYevdaLqhBseODmz/Dp/lqsvKh3DnrEnqmda4IR7sqg5RrgN1hMuss7oHbv28VBssI2TwLPOHtYFgE/yjN1DPZ2fx5WUaunLbLbAyUJ1JtPR8cIIqkkTjT/IGZUUHgIHLvLHa4BmHXiAO4gAnSOGrexDlkcmbbqyUkLrJVTmFByBb1YTBWuRERM7nC+3MU3fYxakJXFbAgMBAAECggEAH2PugabACGfd2wUCqHgb8LyTSMeRJqRBXdlx73kX5gCT5wIPFf9PdFiXJTEgoZflenH/lT7f/tegyOQzMsX4AYFZ1qufPLAN4d5RW0QS4HljFTyQwghem513m8ot1tZgFT9CdeqmorC2sQDZ7kbJWPNQgL3pxlPdPIWiM0kX9+lPeS55T0wxrQKt7hbrG2yxEZlh2O7eDn4Qw5oLweXhFk9N90A0NOwiD/oy2/srxmUw5j4RpSNMYvFyqQ8GyxgDCXJW7IFRPt/sJUPJWM0BHlFCjYlwFNZtEYXZhZc8vqNiSzUKL8t/L4Q7tLlJbcAYtFhSEeej2XbXw/78WXOYOQKBgQD6T2PLPxRmn4qAGRKuMr2KvIVKi/PQQnvOlRpz4WyVw65ViWHjzkBILOfBJmUniHHPGxhltks35z99t5TNkWzwvv0SnFZVpueODlf7D9ZcIqP/jAmNbq1g4aYtwErNUoqZNg3L0Kl9ohcX2D/tgUzOc9ZSanlyGUUM+KVp/1fvvQKBgQDLV+TFFgt67q3vcuSdDyHS1h9HllAyLzkhtHuePPB1hZ4X9o9nyL9PF0Tkgrxrrqx/U4y0hXV3u4Gb9do5hCjUxMGmdR18bzAYDYZ38AivstOViQpHNlsXVgINc7aydsgqDluKDzv1eX5pHMC2G6ql9fB+Z76E4dIx781tGMrK9wKBgA/2DgQA0rk0hS1j6+gFvkZhfRJi2Do4cxKaOWSYREdMggpcR0yyczDnBCQx4ZNixPcS4DxkImm/KjJHCYsOMecsFpEgPPUbPnq1eXC98spPPyvlbxqAqzeZS/Jw55QuJj5czwZnGNe1BUn09Sac7xvfERSSS4o2CEwqcsnOiXn1AoGAHS/1j3CVn0SpRMiKFh047ql7fewOgNWUbskYQWNf5I6UXb4vYWdIRpMBeYdmglJlQjV86eCKNvB8LyW/+CeHMNs5LKp83AKdtaYjWg3jQUz63+j2x7f+pT/cNZAZdk5kdtzuuMdtBNQ2l+Hfq4QXH5FIvuwnRa2gXTidlP5NBYsCgYEAxX2DaPIV9Ei7znDBJqClX6M/AeoxxgbiQyZTy+jJf+aFEVJOsQwAmeRj9Cq5pRrNWUs4VhWqTVu3o40ALNQW3AmLZ+LEGWdTmT7iQah4EFDdom+6LloJ/3Eq3vptM5XUBs3kCHghbWenhiO1PiCaqDf7pByN/2BUeo1aj6FSHLc=";

    //下单链接
    public static final String base_url = "https://api.mch.weixin.qq.com";
    public static final String order_url = "/v3/pay/transactions/jsapi";
    public static final String cert_url = "/v3/certificates";
    public static final String token_url = "https://api.weixin.qq.com/sns/oauth2/access_token";
    //支付回调
    public static final String wx_notify_url = "http://running.zhongaiyisheng.net/ajax/wxNotify";
    public static final String zfb_notify_url = "http://running.zhongaiyisheng.net/ajax/zfbNotify";

    //商户签名工具
    private static Signature signature = null;

    //远程请求工具
    private static CloseableHttpClient httpclient;

    /*
        初始化
     */
    public static void init()
    {
        //http请求工具
        httpclient = HttpClientBuilder.create().build();
        //支付宝初始化
        Factory.setOptions(getOptions());

        //初始化签名工具
        try {
            //微信API证书1
            FileInputStream instream = new FileInputStream(new File(cert_file));
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            try {
                keystore.load(instream, mch_id.toCharArray());
                PrivateKey key = (PrivateKey) keystore.getKey("tenpay certificate", mch_id.toCharArray());
                signature = Signature.getInstance("SHA256withRSA");
                signature.initSign(key);
            } finally {
                instream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //请求微信支付平台证书
        getCert();
    }

    /**
     * 下单
     * @param trade_no
     * @return
     * @throws Exception
     */
    public static Map preparePay(String trade_no, String openid, long money) throws Exception {
        Map result = new HashMap();
        // openid = "oWXaX52D0dw2jHRHKS5e2stHl36s";
        //String openid = getOpenID("051aRG0w3K207V2fhh4w3UjZjN1aRG0w");
        String nonceStr = "" + getCurrTime() + buildRandom(4);

        JSONObject param = new JSONObject();
        param.put("appid", app_id);
        param.put("mchid", mch_id);
        param.put("description", money + "福豆");
        param.put("out_trade_no", trade_no);
        param.put("notify_url", wx_notify_url);
        JSONObject amount = new JSONObject();
        //测试用1分钱
        amount.put("total", money * 100);
        //amount.put("total", money);
        param.put("amount", amount);
        JSONObject payer = new JSONObject();
        payer.put("openid", openid);
        param.put("payer", payer);
        //获取下单ID
        String prepay_id = getPayNo(param.toJSONString(), nonceStr);
        if (StringUtils.isEmpty(prepay_id)) {
            return null;
        }
        //数据传到前端
        String timeStamp = getTimeStamp();
        String _package = "prepay_id=" + prepay_id;
        result.put("appId", app_id);
        result.put("timeStamp", timeStamp);
        result.put("nonceStr", nonceStr);
        result.put("package", _package);
        result.put("signType", "RSA");
        result.put("paySign", getSign(app_id, timeStamp, nonceStr, _package));
        return result;
    }

    /**
     * //支付宝支付
     * @param trade_no
     * @return
     * @throws Exception
     */
    public static String alipay(String trade_no, String subject, long money) throws Exception {
        AlipayTradeWapPayResponse response = Factory.Payment.Wap()
                .pay(subject, trade_no, money + "", "", "http://www.baidu.com");
        if (ResponseChecker.success(response)) {
            System.out.println("调用手机支付成功");
            return response.body;
        } else {
            System.out.println("调用手机支付失败");
            return null;
        }
    }

    //获取微信支付平台证书
    public static void getCert() {
        try {
            HttpGet httpGet = new HttpGet(base_url + cert_url);
            httpGet.setHeader("Authorization", getAuthorization("GET", cert_url, "", getTimeStamp()));
            httpGet.setHeader("Accept", "application/json");
            httpGet.setHeader("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
            httpGet.setHeader("Content-Type", "application/json;charset=utf8");

            CloseableHttpResponse response = httpclient.execute(httpGet);
            String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println("cert json是:"+jsonStr);
            LogUtils.getAccessLog().info("======微信支付证书请求json=== body:" + jsonStr);

            JSONObject bodyObj = JSON.parseObject(jsonStr);
            JSONArray certs = bodyObj.getJSONArray("data");
            if (certs.size() == 0) {    //没有可用证书
                return;
            }
            //最新证书
            JSONObject certObj = certs.getJSONObject(0);
            wx_cert_serial = certObj.getString("serial_no");
            JSONObject encrypt = certObj.getJSONObject("encrypt_certificate");
            byte[] certKey = getCipherBytes(encrypt.getString("associated_data"), encrypt.getString("nonce"), encrypt.getString("ciphertext"));
            byte[] key = parseDERFromPEM(certKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
            //LogUtils.getAccessLog().info("======微信支付证书请求json=== cert key:" + new String(key));
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            X509Certificate cert = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(key));

            wx_signature = Signature.getInstance("SHA256withRSA");
            wx_signature.initVerify(cert);
        } catch (Exception e) {
            LogUtils.getAccessLog().info("======微信支付证书请求初始化key出错=== message:" + e.getMessage());
            e.printStackTrace();
        }
    }

    public static String getPayNo(String params, String nonceStr) throws Exception {
        System.out.println("params json string:" + params);

        HttpPost httpPost = new HttpPost(base_url + order_url);
        httpPost.setHeader("Authorization", getAuthorization("POST", order_url, params, nonceStr));
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
        httpPost.setHeader("Content-Type", "application/json;charset=utf8");
        httpPost.setEntity(new StringEntity(params, "UTF-8"));

        String prepay_id = "";
        CloseableHttpResponse response = httpclient.execute(httpPost);
        String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
        System.out.println("json是:"+jsonStr);

        if(jsonStr.indexOf("FAIL")!=-1){
            return prepay_id;
        }
        JSONObject object = JSON.parseObject(jsonStr);
        return object.getString("prepay_id");
    }

    public static String getOpenID(String code){
        HttpGet httpGet = new HttpGet(token_url + "?appid=" + app_id + "&secret=" + app_secret + "&code=" + code + "&grant_type=authorization_code");
        httpGet.setHeader("Content-Type", "application/json;charset=utf8");

        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(httpGet);
            String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println("json是:"+jsonStr);
            JSONObject object = JSON.parseObject(jsonStr);
            return object.getString("openid");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args){
        try {

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 验证是否微信回调请求
     * @param request
     * @return
     */
    public static boolean checkWxRequest(HttpServletRequest request, String body) {
        String serial = request.getHeader("Wechatpay-Serial");
        if(wx_signature == null) {
            LogUtils.getAccessLog().info("======微信支付平台证书未初始化");
            return false;
        }
        if (!wx_cert_serial.equals(serial)) {  //检查证书序列号
            LogUtils.getAccessLog().info("======微信回调请求序列号不匹配 serial:" + serial);
            return false;
        }
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signStr = request.getHeader("Wechatpay-Signature");
        String message = timestamp + "\n"
                + nonce + "\n"
                + body + "\n";
        LogUtils.getAccessLog().info("======微信回调请求header=== timestamp:" + timestamp + " nonce:" + nonce + " signStr:" + signStr);
        try {
            wx_signature.update(message.getBytes("utf-8"));
            return wx_signature.verify(Base64.getDecoder().decode(signStr));
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.getAccessLog().error("======微信回调请求签名解密失败 message:" + e.getMessage());
        }
        return false;
    }

    /**
     * 验证是否支付宝回调请求
     * @param request
     * @return
     */
    public static boolean checkZfbRequest(ZfbNotifyRequest request, HttpServletRequest servletRequest) {
        //判断app_id
        if (!zfb_app_id.equals(request.getApp_id())) return false;
        //判断seller_id
        if (!zfb_seller_id.equals(request.getSeller_id())) return false;
        //验签
        Map requestParams = servletRequest.getParameterMap();
        List<String> keys = new ArrayList<>();
        Map<String, String> params = new HashMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            //if ("sign".equals(name) || "sign_type".equals(name)) continue;
            keys.add(name);
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }

        try {
            //验签
            LogUtils.getAccessLog().info("======支付回调请求验证签名字符串 message:" + JSON.toJSONString(params));
            return Factory.Payment.Common().verifyNotify(params);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtils.getAccessLog().error("======支付回调请求验证签名失败 message:" + e.getMessage());
        }

        return false;
    }


    /**
     * 解密notify_url包体
     * @param
     * @return
     */
    public static byte[] getCipherBytes(String associatedData, String nonce, String ciphertext) throws Exception{
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

        SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(), "AES");
        byte[] nonceBytes = nonce.getBytes();
        GCMParameterSpec spec = new GCMParameterSpec(128, nonceBytes);

        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        cipher.updateAAD(associatedData.getBytes());

        return cipher.doFinal(Base64.getDecoder().decode(ciphertext));
    }

    /**
     * 解密notify_url包体
     * @param
     * @return
     */
    public static String decryptBodyString(String associatedData, String nonce, String ciphertext) throws Exception{
        return new String(getCipherBytes(associatedData, nonce, ciphertext), "utf-8");
    }

    /**
     * 获取请求签名
     * @param params
     * @return
     * @throws Exception
     */
    public static String getSign(String ... params) throws Exception {
        StringBuffer sb = new StringBuffer();
        for (String str : params) {
            sb.append(str).append("\n");
        }
        signature.update(sb.toString().getBytes("utf-8"));
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * 请求权限签名
     * @param
     * @return
     * @throws Exception
     */
    public static String getAuthorization(String method, String url, String body, String nonceStr) throws Exception {
        String timestamp = getTimeStamp();
        String message = method + "\n"
                + url + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
        signature.update(message.getBytes("utf-8"));
        String sign = Base64.getEncoder().encodeToString(signature.sign());

        return sign_format + " "
                + "mchid=\"" + mch_id + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + cert_serial + "\","
                + "signature=\"" + sign + "\"";
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length
     *            int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }

    public static String getTimeStamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }

    public static String ReadAsChars(HttpServletRequest request)
    {

        BufferedReader br = null;
        StringBuilder sb = new StringBuilder("");
        try
        {
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null)
            {
                sb.append(str);
            }
            br.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (null != br)
            {
                try
                {
                    br.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    public static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
        String data = new String(pem);
        String[] tokens = data.split(beginDelimiter);
        tokens = tokens[1].split(endDelimiter);
        return DatatypeConverter.parseBase64Binary(tokens[0]);
    }

    /**
     * 支付宝支付配置
     * @return
     */
    private static Config getOptions() {
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = "openapi.alipay.com";
        config.signType = "RSA2";

        config.appId = zfb_app_id;
        config.merchantPrivateKey = zfb_private_key;
        config.alipayPublicKey = zfb_public_key;

        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = zfb_notify_url;

        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        //config.encryptKey = "<-- 请填写您的AES密钥，例如：aa4BtZ4tspm2wnXLb1ThQA== -->";

        return config;
    }
}
